package org.medusa.jsr223;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.HashMap;
import java.util.Map;

import javax.script.Compilable;
import javax.script.CompiledScript;
import javax.script.Invocable;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class Jsr223Engine {
	//private static Map<String,Jsr223Engine> inst = new HashMap<String,Jsr223Engine>();
	private ScriptEngine engine;
	private ScriptContext global_context;
	private Invocable inv;
	private Map<String, CompiledScript> script_map;
	
	protected Jsr223Engine(ScriptEngineManager factory, String file_ext_name) {
		this.script_map = new HashMap<String, CompiledScript>();
		//ScriptEngineManager factory = new ScriptEngineManager();
		
		this.engine = factory.getEngineByExtension(file_ext_name);
		this.global_context = engine.getContext();
		this.inv = (Invocable) this.engine;
	}
	/*
	private static Jsr223Engine get_inst(String file_ext_name) {
		if (Jsr223Engine.inst.get(file_ext_name)==null) {
			Jsr223Engine.inst.put(file_ext_name, new Jsr223Engine(file_ext_name));
		}
		
		return Jsr223Engine.inst.get(file_ext_name);
	}*/
	
	
	public ScriptEngine GetScriptEngine() {
		return this.engine;
	}

	public ScriptContext GetGlobalContext() {
		return this.global_context;
	}
	
	public void SetContext(ScriptContext context) {
		this.engine.setContext(context);
	}
	
	public ScriptContext GetContext() {
		return this.engine.getContext();
	}

	public CompiledScript ReadScriptFile(String script_file_path_name) {
		Compilable compilable = (Compilable) this.engine;
	    CompiledScript compiledScript =null;
	    
	    if (!this.script_map.containsKey(script_file_path_name)) {
	    	try {
				compiledScript = compilable.compile(new FileReader(script_file_path_name));
				this.script_map.put(script_file_path_name, compiledScript);
			} catch (FileNotFoundException e1) {
				e1.printStackTrace();
			} catch (ScriptException e1) {
				e1.printStackTrace();
			}
	    }else
	    	compiledScript = this.script_map.get(script_file_path_name);
		
		return compiledScript;
	}
	
	public CompiledScript CompileScriptString(String file_ext_name, String script_string) {
		Compilable compilable = (Compilable) this.engine;
	    CompiledScript compiledScript =null;
	    
	    try {
			compiledScript = compilable.compile(script_string);
		}catch (ScriptException e1) {
			e1.printStackTrace();
		}

		return compiledScript;
	}
	
	public Object ImportScriptFile(String script_file_path_name) {
		CompiledScript script = this.ReadScriptFile(script_file_path_name);
		Object result = null;
		
		try {
			result =  script.eval();
		} catch (ScriptException e) {
			e.printStackTrace();
		}
		
		return result;
	}
	
	public Object CallFunction(String name, Object... args) {
		Object result=null;
		ScriptContext old = this.engine.getContext();
		this.engine.setContext(this.global_context);//this.IntoContext();//------------
		
		try {
			result = this.inv.invokeFunction(name, args);
		} catch (ScriptException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		}
		
		this.engine.setContext(old); //this.LeaveContext();//-------------
		
		return result;
	}
	
	public void SetAttribute(String file_ext_name, String attr_name, Object value) {
		//两种方法一样
		//1.
		//this.engine.put(attr_name, value);
		//2
		this.global_context.setAttribute(attr_name, value,
				ScriptContext.ENGINE_SCOPE);
	}

	
}
